#
# RCON Anywhere - http://www.rcon-anywhere.net
# Desktop tool
#
# Created by Lucas van Dijk (www.return1.net)
#
# Package Lucky Bot Plugin
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
#
# $Id: __init__.py 77 2009-06-06 13:00:28Z luckyluke56 $
#

from luckybot import path
from luckybot.bot.plugins import Plugin
from luckybot.luckynet.protocols.irc import Format
import sqlite3 as sqlite
import re
import socket

from serverstats import Gameserver, GameserverException
from serverstats.protocols.hl import SourceProtocol

class BaseTemplate(object):
	"""
		Server vars -> irc message mapper
	"""

	def __init__(self, server):
		self.server = server

	def get_vars(self):
		raise NotImplementedError

class SourceTemplate(BaseTemplate):
	"""
		Template for source servers
	"""

	def get_vars(self):
		return {
			'ip': self.server.addr[0],
			'port': str(self.server.addr[1]),
			'name': self.server.info['server_name'],
			'map': self.server.info['map'],
			'num_players': str(self.server.info['num_players']),
			'max_players': str(self.server.info['max_players'])
		}


class GameserverPlugin(Plugin):
	"""
		Plugin which gets information about a gameserver
	"""

	PLUGIN_INFO = {
		'name': 'Gameserver Stats',
		'description': 'Retrieves information about a given gameserver',
		'authors': ['Lucas van Dijk'],
		'version': '1.0',
		'website': 'http://www.return1.net'
	}

	SUPPORTED_GAMES = {
		'source': ('source', 'css', 'dods')
	}

	PROTOCOL_MAP = {
		'source': SourceProtocol
	}

	def initialize(self):
		self.register_command('gameserver', self.get_gameserver_stats, help=self.lang.get('gameserver_help'), args="presetname|game ip:port")
		self.register_command('addgameserver', self.add_gameserver, help=self.lang.get('addgameserver_help'), args="game ip:port")
		self.register_command('reviewserver', self.review_servers, help=self.lang.get('review_help'), args="[id yes|no]")

		# Setup database connection
		self.connection = sqlite.connect(path.get_personal_file('gameserver.db'))
		self.connection.isolation_level = None
		self.connection.row_factory = sqlite.Row

		sql = """
		CREATE TABLE IF NOT EXISTS servers (
			id integer NOT NULL PRIMARY KEY,
			name varchar(50),
			game varchar(15),
			ip varchar(255),
			port integer(10),
			moderated integer(1) default 0
		);
		"""

		self.connection.execute(sql)

	def get_gameserver_stats(self, event):
		"""
			Called when gameserver command is used
		"""

		try:
			args = event.message.bot_args.split()

			if len(args) == 1:
				# preset
				name = Format.remove(args[0])

				sql = "SELECT * FROM servers WHERE name = ? AND moderated = 1 LIMIT 1"
				row = self.connection.execute(sql, (args[0],)).fetchone()

				if not row:
					raise GameserverException, self.lang.get('server_does_not_exists')

				ip = row['ip']
				port = row['port']
				game = row['game']
			elif len(args) == 2:
				addr = args[1].split(':', 1)
				ip = addr[0]
				try:
					port = int(addr[1])
				except:
					raise GameserverException, self.lang.get('invalid_port')

				game = args[0]
			else:
				raise GameserverException, self.lang.get('gameserver_syntax')


			protocol = None
			# check if game is supported
			for game_type, aliasses in self.SUPPORTED_GAMES.iteritems():
				if game in aliasses:
					protocol = self.PROTOCOL_MAP[game_type]
					break

			if protocol == None:
				raise GameserverException, self.lang.get('not_supported')

			# Let's get some stats
			try:
				server = Gameserver(protocol, (ip, port))
				server.query('info')
			except socket.error:
				raise GameserverException, self.lang.get('could_not_connect')

			template = SourceTemplate(server)
			event.channel.send_pm(self.lang.get('gameserver_template1', template.get_vars()))
			event.channel.send_pm(self.lang.get('gameserver_template2', template.get_vars()))
		except GameserverException, e:
			event.user.send_notice(str(e))

	def add_gameserver(self, event):
		"""
			Add a game server as preset
		"""

		args = Format.remove(event.message.bot_args).split()

		try:
			if len(args) != 3:
				raise GameserverException, self.lang.get('addgameserver_syntax')

			# Check for supported game
			game = None
			for game_type, aliasses in self.SUPPORTED_GAMES.iteritems():
				if args[1] in aliasses:
					game = game_type
					break

			if game == None:
				raise GameserverException, self.lang.get('not_supported')

			addr = args[2].split(':', 1)
			ip = addr[0]
			try:
				port = int(addr[1])
			except:
				raise GameserverException, self.lang.get('invalid_port')

			try:
				# Check if we can connect to it
				# Source protocol is here just as dummy protocol used
				Gameserver(SourceProtocol, (ip, port))
			except socket.gaierror:
				raise GameserverException, self.lang.get('could_not_connect')

			name = args[0]
			regexp = re.compile('^[a-zA-Z0-9\-_.]+$')
			if not regexp.match(name):
				raise GameserverException, self.lang.get('invalid_name')

			# Check if name already exists
			sql = "SELECT * FROM servers WHERE name = ?"
			row = self.connection.execute(sql, (name,)).fetchone()

			if row:
				raise GameserverException, self.lang.get('already_exists')

			sql = """
			INSERT INTO servers (name, ip, port, game, moderated) VALUES (
				?, ?, ?, ?, ?)
			"""

			self.connection.execute(sql, (name, ip, port, game, 1 if event.user.check_logged_in() else 0))
			if event.user.check_logged_in():
				event.user.send_notice(self.lang.get('add_success'))
			else:
				event.user.notice(self.lang.get('needs_review'))
		except GameserverException, e:
			event.user.send_notice(str(e))

	def review_servers(self, event):
		"""
			Called when !reviewservers command is used

			With this command, bot admins can review added servers. They
			can view the current unreviewed servers and allow or deny an
			added feed,
		"""

		if not event.user.check_logged_in():
			event.user.send_notice(self.lang.get('access_denied'))
			return

		args = event.message.bot_args.split()

		if len(args) == 0:
			# Display a list of unreviewed feeds
			cursor = self.connection.execute('SELECT * FROM servers WHERE moderated = 0 ORDER BY id ASC')

			event.user.send_notice(self.lang.get('unreviewed_servers'))
			for row in cursor:
				vars = {
					'id': str(row['id']),
					'game': row['game'],
					'ip': row['ip'],
					'port': str(row['port']),
					'name': row['name'],
				}

				event.user.send_notice(self.lang.get('item_template', vars))
		elif len(args) == 2:
			# Check for valid feed ID
			if not args[0].isdigit():
				event.user.send_notice(self.lang.get('invalid_id'))
				return

			# Review a feed
			if args[1] in ['ok', 'good', 'yes']:
				self.connection.execute('UPDATE servers SET moderated = 1 WHERE id = ?', (args[0],))
				event.user.send_notice(self.lang.get('server_reviewed'))
			elif args[1] in ['wrong', 'bad', 'no']:
				self.connection.execute('DELETE FROM servers WHERE id = ?', (args[0],))
				event.user.send_notice(self.lang.get('server_reviewed'))
			else:
				event.user.send_notice(self.lang.get('review_syntax'))
		else:
			event.user.send_notice(self.lang.get('review_syntax'))

	def destroy(self):
		self.connection.close()


